package gm.tcpping;

import java.io.*;

public final class Message implements Serializable {
    
    // ===========================================================
    // Constants
    // ===========================================================

    private static final int MAX_FILL_STEP = 100;
    private static final byte[] FILLER = new byte[TCPPing.BUFFER_SIZE];
    private static final int OUTPUT_BUFFER_BLOCK_HEADER_SIZE = 2;
    
    // ===========================================================
    // Fields
    // ===========================================================
    
    private static final long serialVersionUID = 2006637215107958708L;
    private long mMessageId;
    private int mMessageSize;
    private long mPitcherTime;
    private long mCatcherTime;
    private long mPitcherReturnTime;

    // ===========================================================
    // Constructors
    // ===========================================================
    
    public Message(
            long messageId,
            int messageSize,
            long pitcherTime) {
        
        mMessageId = messageId;
        mMessageSize = messageSize;
        mPitcherTime = pitcherTime;
        mCatcherTime = 0;
        mPitcherReturnTime = 0;
    }
    
    // ===========================================================
    // Methods for/from SuperClass/Interfaces
    // ===========================================================

    @Override
    public String toString() {
        return "Message [mMessageId=" + mMessageId + ", mMessageSize="
                + mMessageSize + ", mPitcherTime=" + mPitcherTime
                + ", mCatcherTime=" + mCatcherTime + ", mPitcherReturnTime="
                + mPitcherReturnTime + "]";
    }
    
    // ===========================================================
    // Methods
    // ===========================================================

    public static byte[] serialize(Message message) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos) ;
        oos.writeObject(message);
        oos.flush();
        byte[] buf = bos.toByteArray();

        // fill message to defined length
        int messageSize = message.getMessageSize();
        while (buf.length < messageSize) {
            int fillSize = Math.min(messageSize - buf.length - OUTPUT_BUFFER_BLOCK_HEADER_SIZE, MAX_FILL_STEP);
            oos.write(FILLER, 0, fillSize);
            oos.flush();
            buf = bos.toByteArray();
        }
        
        oos.close();
        bos.close();
        
        return buf;
    }
    
    public static Message deserialize(byte[] buf) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bis = new ByteArrayInputStream(buf);
        ObjectInputStream ois = new ObjectInputStream(bis);
        Message message = (Message)ois.readObject();
        ois.close();
        bis.close();
        
        return message;
    }
    
    public void offsetCatcherTime(long offset) {
        mCatcherTime += offset;
    }
    
    public long getPitcherToCatcher() {
        return mCatcherTime - mPitcherTime;
    }
    
    public long getCatcherToPitcher() {
        return mPitcherReturnTime - mCatcherTime;
    }
    
    public long getRTT() {
        return mPitcherReturnTime - mPitcherTime;
    }
    
    // ===========================================================
    // Inner and Anonymous Classes
    // ===========================================================

    // ===========================================================
    // Getter & Setter
    // ===========================================================
    
    public long getCatcherTime() {
        return mCatcherTime;
    }

    public void setCatcherTime(long catcherTime) {
        mCatcherTime = catcherTime;
    }

    public long getPitcherReturnTime() {
        return mPitcherReturnTime;
    }

    public void setPitcherReturnTime(long pitcherReturnTime) {
        mPitcherReturnTime = pitcherReturnTime;
    }

    public long getMessageId() {
        return mMessageId;
    }

    public int getMessageSize() {
        return mMessageSize;
    }

    public long getPitcherTime() {
        return mPitcherTime;
    }
}